﻿
using System.Xml;
using System;
using System.Net;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
public class SoapParameters
{

    /// <summary>https://mydomain.com:7071/service/admin/soap/</summary>
    internal string serviceAdminSoapUri { get; set; }

    /// <summary>format: abc@xyz.com (administrator account that can make changes)</summary>
    internal string adminUsername { get; set; }

    /// <summary>(administrator account that can make changes)</summary>
    internal string adminPassword { get; set; }

    internal string authToken { get; set; }

    /// <summary>format: abc@xyz.com (account to which to perform soapCommand on)</summary>
    public string accountUsername { get; set; }

    public string accountFirstName { get; set; }

    public string accountLastName { get; set; }

    public string accountDisplayName
    {
        get { return string.Format("{0} {1}", accountFirstName, accountLastName); }
    }

    internal string accountId { get; set; }

    public SoapParameters()
    {
        string impersonateDomain = System.Configuration.ConfigurationManager.AppSettings["Domain.Settings.ImpersonateDomain"];
        string impersonateUsername = System.Configuration.ConfigurationManager.AppSettings["Domain.Settings.ImpersonateUsername"];
        string impersonatePassword = System.Configuration.ConfigurationManager.AppSettings["Domain.Settings.ImpersonatePassword"];
        string zimbraServiceAdminSoapUri = System.Configuration.ConfigurationManager.AppSettings["Domain.Settings.ZimbraServiceAdminSoapUri"];
        adminUsername = string.Format("{0}@{1}", impersonateUsername, impersonateDomain);
        adminPassword = impersonatePassword;
        serviceAdminSoapUri = zimbraServiceAdminSoapUri;
    }

}

    // http://zimbra.svn.sourceforge.net/vi...soap-admin.txt

public class AccountService
    {
    public AccountService()
    {
        
    }
    public static bool AcceptAllCertificatePolicy(object sender,
                                                X509Certificate certificate,
                                                X509Chain chain,
                                                SslPolicyErrors sslPolicyErrors)
    {
        return true;
    }
        private enum SoapMessages
        {
            AuthenticationToken,
            CreateAccount,
            RemoveAccount,
            Account
        }

        /// <summary>Get First and Last Name of an account based on email address</summary>
        /// <param name="param">just include an email address</param>
        public static SoapParameters Read(SoapParameters param)
        {
            GetAdministratorAuthenticationToken(param);
            GetAccount(param);
            return param;
        }

        public static void Create(SoapParameters param)
        {
            GetAdministratorAuthenticationToken(param);
            CreateAccountRequest(param);
        }

        /// <summary>Remove an account based on email address</summary>
        /// <param name="param">just include an email address</param>
        public static void Delete(SoapParameters param)
        {
            GetAdministratorAuthenticationToken(param);
            GetAccount(param);
            RemoveAccountRequest(param);
        }

        private static void GetAdministratorAuthenticationToken(SoapParameters param)
        {
            XmlDocument doc = GetResponseDoc(param, SoapMessages.AuthenticationToken);
            param.authToken = doc.GetElementsByTagName("authToken").Item(0).InnerXml;
        }

        private static void CreateAccountRequest(SoapParameters param)
        {
            XmlDocument doc = GetResponseDoc(param, SoapMessages.CreateAccount);
            try
            {
                string value = doc.GetElementsByTagName("CreateAccountResponse").Item(0).ChildNodes[0].Attributes["id"].Value;
                Guid guid = new Guid(value);
                param.accountId = guid.ToString();
            }
            catch
            {
                throw new Exception("Account Did Not Appear To Be Created.");
            }
        }

        public static void GetAccount(SoapParameters param)
        {
            XmlDocument doc = GetResponseDoc(param, SoapMessages.Account);
            param.accountFirstName = null;
            param.accountLastName = null;
            param.accountId = null;
            string tag = "GetAccountResponse";
            string attr;
            string value;
            int i = 0;
            int max = doc.GetElementsByTagName(tag).Item(0).ChildNodes[0].ChildNodes.Count;

            for (int x = 0; x < doc.GetElementsByTagName(tag).Item(0).ChildNodes[0].Attributes.Count; x++)
            {
                attr = doc.GetElementsByTagName(tag).Item(0).ChildNodes[0].Attributes[x].Name.ToLower();
                value = doc.GetElementsByTagName(tag).Item(0).ChildNodes[0].Attributes[x].InnerText;
                switch (attr)
                {
                    case "id":
                        try
                        {
                            Guid guid = new Guid(value);
                            param.accountId = guid.ToString();
                        }
                        catch { }
                        break;
                }
            }

            while (i < max)
            {
                attr = doc.GetElementsByTagName("GetAccountResponse").Item(0).ChildNodes[0].ChildNodes[i].Attributes[0].Value.ToLower();
                value = doc.GetElementsByTagName("GetAccountResponse").Item(0).ChildNodes[0].ChildNodes[i].InnerText;
                switch (attr)
                {
                    case "givenname":
                        param.accountFirstName = value;
                        break;
                    case "sn":
                        param.accountLastName = value;
                        break;
                }
                i = (param.accountFirstName == null || param.accountLastName == null) ? i + 1 : max;
            }

        }

        private static void RemoveAccountRequest(SoapParameters param)
        {
            XmlDocument doc = GetResponseDoc(param, SoapMessages.RemoveAccount);
            if (doc == null)
                throw new Exception("Unsure if account was removed, please have administrator check.");
        }

        private static XmlDocument GetResponseDoc(SoapParameters param, SoapMessages message)
        {
            ServicePointManager.ServerCertificateValidationCallback += AcceptAllCertificatePolicy;
            string soapMessage = SoapMessageBuilder(message, param);
            XmlDocument requestDoc = new XmlDocument();
            requestDoc.LoadXml(soapMessage);
            HttpWebRequest request = HttpWebRequestBuilder(param.serviceAdminSoapUri);
            //ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

            Stream stm = request.GetRequestStream();
            requestDoc.Save(stm);
            stm.Close();
            
            WebResponse resp = request.GetResponse();
            stm = resp.GetResponseStream();
            StreamReader r = new StreamReader(stm);
            XmlDocument responseDoc = new XmlDocument();
            responseDoc.LoadXml(r.ReadToEnd());
            return responseDoc;
        }

        private static HttpWebRequest HttpWebRequestBuilder(string serviceAdminSoapUri)
        {
            HttpWebRequest retVal = (HttpWebRequest)WebRequest.Create(serviceAdminSoapUri);
            retVal.Headers.Add("SOAPAction", "\"\"");
            retVal.ContentType = "text/xml;charset=\"utf-8\"";
            retVal.Accept = "text/xml";
            retVal.Method = "POST";
            return retVal;
        }

        private static string SoapMessageBuilder(SoapMessages message, SoapParameters param)
        {
            string retVal = "";
            switch (message)
            {
                case SoapMessages.AuthenticationToken:
                    retVal = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">" +
                    "<soap:Header>" +
                    "<context xmlns=\"urn:zimbra\"/>" +
                    "</soap:Header>" +
                    "<soap:Body>" +
                    "<AuthRequest xmlns=\"urn:zimbraAdmin\">" +
                    "<name>" + param.adminUsername + "</name>" +
                    "<password>" + param.adminPassword + "</password>" +
                    "</AuthRequest>" +
                    "</soap:Body>" +
                    "</soap:Envelope>";
                    break;
                case SoapMessages.CreateAccount:
                    retVal = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">" +
                    "<soap:Header>" +
                    "<context xmlns=\"urn:zimbra\">" +
                    "<authToken>" + param.authToken + "</authToken>" +
                    "</context>" +
                    "</soap:Header>" +
                    "<soap:Body>" +
                    "<CreateAccountRequest xmlns=\"urn:zimbraAdmin\">" +
                    "<name>" + param.accountUsername + "</name>" +
                    "<password></password>" +
                    "<a n=\"givenName\">" + param.accountFirstName + "</a>" +
                    "<a n=\"sn\">" + param.accountLastName + "</a>" +
                    "<a n=\"displayName\">" + param.accountDisplayName + "</a>" +
                    "</CreateAccountRequest>" +
                    "</soap:Body>" +
                    "</soap:Envelope>";
                    break;
                case SoapMessages.RemoveAccount:
                    retVal = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">" +
                    "<soap:Header>" +
                    "<context xmlns=\"urn:zimbra\">" +
                    "<authToken>" + param.authToken + "</authToken>" +
                    "</context>" +
                    "</soap:Header>" +
                    "<soap:Body>" +
                    "<DeleteAccountRequest xmlns=\"urn:zimbraAdmin\">" +
                    "<id>" + param.accountId + "</id>" +
                    "</DeleteAccountRequest>" +
                    "</soap:Body>" +
                    "</soap:Envelope>";
                    break;
                case SoapMessages.Account:
                    retVal = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">" +
                    "<soap:Header>" +
                    "<context xmlns=\"urn:zimbra\">" +
                    "<authToken>" + param.authToken + "</authToken>" +
                    "</context>" +
                    "</soap:Header>" +
                    "<soap:Body>" +
                    "<GetAccountRequest xmlns=\"urn:zimbraAdmin\">" +
                    "<account by=\"name\">" + param.accountUsername + "</account>" +
                    "</GetAccountRequest>" +
                    "</soap:Body>" +
                    "</soap:Envelope>";
                    break;
            }
            return retVal;
        }

    }
